﻿package kliment.controls {
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import kliment.controls.ControlsAbstract;
	import kliment.display.draw.style.LineStyle;
	import kliment.events.MouseGestureEvent;
	import kliment.utils._Sprite;
	import kliment.utils.IntervalTimer;
	
	/**
     * Dispatched when mouse slide right.
     */
    [Event(name = 'slide_right', type = 'kliment.events.MouseGestureEvent')]
	/**
     * Dispatched when mouse slide left.
     */
    [Event(name = 'slide_left', type = 'kliment.events.MouseGestureEvent')]
	/**
     * Dispatched when mouse slide down.
     */
    [Event(name = 'slide_down', type = 'kliment.events.MouseGestureEvent')]
	/**
     * Dispatched when mouse slide up.
     */
    [Event(name = 'slide_up', type = 'kliment.events.MouseGestureEvent')]
	/**
     * Dispatched when ...
     */
    [Event(name = 'drag', type = 'kliment.events.MouseGestureEvent')]
	/**
     * Dispatched when ...
     */
    [Event(name = 'start', type = 'kliment.events.MouseGestureEvent')]
	/**
     * Dispatched when ...
     */
    [Event(name = 'finish', type = 'kliment.events.MouseGestureEvent')]
	
	/**
	 * ...
	 * @author	Kliment
	 * @version 1.2.11
	 * 
	 * class name: kliment.controls.MouseGesture
	 */
	public class MouseGesture extends ControlsAbstract {
		private var _intervalTimer:IntervalTimer;
		private var _target_mc:Sprite;
		private var _startPoint:Point;
		private var _lastPoint:Point;
		private var _endPoint:Point;
		private var _points:Array;
		private var _isPress:Boolean = false;
		private var _draw_mc:Sprite; 
		private var _delay:Number;
		private var _stage:Stage;
		public var minSize:Number;
		public var enableDraw:Boolean = true;
		public var lineStyle:LineStyle;
		
		
		public function MouseGesture(space_mc:Sprite, delay:Number = 300, minSize:Number = 200) {
			super();
			_target_mc = space_mc;
			_delay = delay;
			this.minSize = minSize;
			lineStyle = new LineStyle(1, 0x000000);
			
			_stageHandler(new Event((_target_mc.stage)?Event.ADDED_TO_STAGE:Event.REMOVED_FROM_STAGE));
		}
		
		private function _stageHandler(event:Event):void {
			switch(event.type) {
				case Event.ADDED_TO_STAGE:
					_init();
					_target_mc.removeEventListener(Event.ADDED_TO_STAGE, _stageHandler);
					_target_mc.addEventListener(Event.REMOVED_FROM_STAGE, _stageHandler);
				break;
				
				case Event.REMOVED_FROM_STAGE:
					_uninit();
					_target_mc.removeEventListener(Event.REMOVED_FROM_STAGE, _stageHandler);
					_target_mc.addEventListener(Event.ADDED_TO_STAGE, _stageHandler);
				break;
			}
		}
		
		private function _init():void {
			_isPress = false;
			_intervalTimer = new IntervalTimer(_checkInterval, _delay, false, false);
			_stage = _target_mc.stage;
			_stage.addEventListener(MouseEvent.MOUSE_DOWN, _mouseHandler);
		}
		
		private function _mouseHandler(event:MouseEvent):void {
			switch(event.type) {
				case MouseEvent.MOUSE_DOWN:
					if (!_enable || !_stage) 
						return;
					
					_points = new Array();
					_endPoint = null;
					_startPoint = new Point(_target_mc.mouseX, _target_mc.mouseY);
					_lastPoint = _startPoint;
					_removeDraw();
					
					if (_Sprite.hitTestBoundsMouse(_target_mc)) {
						_isPress = true;
						if (enableDraw) {
							_draw_mc = new Sprite();
							_draw_mc.graphics.lineStyle(lineStyle.width, lineStyle.color, lineStyle.alpha, lineStyle.pixelHinting, lineStyle.scaleMode, lineStyle.caps, lineStyle.joints, lineStyle.miterLimit);
							_draw_mc.graphics.moveTo(_draw_mc.mouseX, _draw_mc.mouseY);
							if (_target_mc.root)
								(_target_mc.root as Sprite).addChild(_draw_mc);
						}
						_intervalTimer.start();
					} else if (!_target_mc){
						remove();
					}
					_target_mc.stage.addEventListener(MouseEvent.MOUSE_UP, _mouseHandler);
					_target_mc.stage.addEventListener(MouseEvent.MOUSE_MOVE, _mouseHandler);
					dispatchEvent(new MouseGestureEvent(MouseGestureEvent.START));
				break;
				
				case MouseEvent.MOUSE_UP:
					_endPoint = new Point(_target_mc.mouseX, _target_mc.mouseY);
					_isPress = false;
					_target_mc.stage.removeEventListener(MouseEvent.MOUSE_UP, _mouseHandler);
					_target_mc.stage.removeEventListener(MouseEvent.MOUSE_MOVE, _mouseHandler);
					dispatchEvent(new MouseGestureEvent(MouseGestureEvent.FINISH));
				break;
				
				case MouseEvent.MOUSE_MOVE:
					if (_isPress) {
						var pos:Point = new Point(_target_mc.mouseX, _target_mc.mouseY);
						var evt:MouseGestureEvent = new MouseGestureEvent(MouseGestureEvent.DRAG);
						evt.dX = pos.x - _lastPoint.x;
						evt.dY = pos.y - _lastPoint.y;
						_lastPoint = pos;
						if (_points) {
							_points.push(_lastPoint);
							if (_target_mc && enableDraw) {
								_draw_mc.graphics.lineTo(_draw_mc.mouseX, _draw_mc.mouseY);
							}
						}
						dispatchEvent(evt);
					}
				break;
			}
		}
		
		private function _checkInterval():void {
			_removeDraw();
			_intervalTimer.stop();
			//_isPress = false;
			if (_endPoint) 
				_checkGesture();
			_points = null;
		}
		
		private function _checkGesture():void {
			var width:Number = Math.abs(_startPoint.x - _endPoint.x);
			var height:Number = Math.abs(_startPoint.y - _endPoint.y);
			
			if (width > height) { 						// horizontal
				if (width >= minSize) {
					if (_startPoint.x > _endPoint.x) 	// to left
						dispatchEvent(new MouseGestureEvent(MouseGestureEvent.SLIDE_LEFT, false, false, width));
					else 								// to right
						dispatchEvent(new MouseGestureEvent(MouseGestureEvent.SLIDE_RIGHT, false, false, width));
				}
			} else if (width < height) { 				// vertical
				if (height >= minSize) {
					if (_startPoint.y > _endPoint.y) 	// to up
						dispatchEvent(new MouseGestureEvent(MouseGestureEvent.SLIDE_UP, false, false, height));
					else 								// to down
						dispatchEvent(new MouseGestureEvent(MouseGestureEvent.SLIDE_DOWN, false, false, height));
				}
			}
		}
		
		override public function set enable(value:Boolean):void {
			if (super.enable == value) return;
			if (!value) {
				if (_intervalTimer)
					_intervalTimer.stop();
				_endPoint = null;
				_startPoint = null;
			}
			super.enable = value;
		}
		
		public function get delay():Number { return _delay; }
		public function set delay(value:Number):void {
			_delay = value;
			_intervalTimer.delay = _delay;
		}
		
		private function _removeDraw():void {
			if (_draw_mc && _draw_mc.parent)
				_draw_mc.parent.removeChild(_draw_mc);
		}
		
		private function _uninit():void {
			_isPress = false;
			_removeDraw();
			if (_stage) {
				_stage.removeEventListener(MouseEvent.MOUSE_DOWN, _mouseHandler);
				_stage.removeEventListener(MouseEvent.MOUSE_UP, _mouseHandler);
				_stage.removeEventListener(MouseEvent.MOUSE_MOVE, _mouseHandler);
			}
			if (_intervalTimer)
				_intervalTimer.stop();
		}
		
		public function remove():void {
			_uninit();
			_target_mc.removeEventListener(Event.ADDED_TO_STAGE, _stageHandler);
			_target_mc.removeEventListener(Event.REMOVED_FROM_STAGE, _stageHandler);
		}
	}

}